// ChatClientDlg.cpp : implementation file
//

#include "stdafx.h"
#include "Chat.h"
#include "ChatRoomClientDlg.h"
#include "ChatClientDoc.h"
#include "ChatClientLobbyDlg.h"

// CChatClientDlg dialog

IMPLEMENT_DYNAMIC(CChatRoomClientDlg, CDialog)

CChatRoomClientDlg::CChatRoomClientDlg(CWnd* pParent /*=NULL*/)
	: CChatClientBaseDlg(CChatRoomClientDlg::IDD, pParent)
	, m_chatInput(_T(""))	
{
	
}

CChatRoomClientDlg::~CChatRoomClientDlg()
{
	
}

void CChatRoomClientDlg::DoDataExchange(CDataExchange* pDX)
{
	CChatClientBaseDlg::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_LIST_CHATOUTPUT, m_chatOutput);
	DDX_Text(pDX, IDC_EDIT_CHATINPUT, m_chatInput);
	DDX_Control(pDX, IDC_LIST_JOINED_USER, m_joinedUsers);
}


BEGIN_MESSAGE_MAP(CChatRoomClientDlg, CDialog)
	ON_BN_CLICKED(IDC_BUTTON_SEND, &CChatRoomClientDlg::OnBnClickedButtonSend)
	ON_BN_CLICKED(IDC_BUTTON_EXIT_ROOM, &CChatRoomClientDlg::OnBnClickedButtonExitRoom)
	ON_WM_DESTROY()
END_MESSAGE_MAP()


// CChatClientDlg message handlers

void CChatRoomClientDlg::OnBnClickedButtonSend()
{
	// TODO: Add your control notification handler code here
	UpdateData(TRUE);	

	if (m_chatInput.GetLength() == 0)
	{
		return;
	}
	
	RakNet::BitStream bitStream;
	bitStream.Write(ChatReq().GetMessageID());
	RakNet::WriteStringToBitStream(bitStream, m_chatInput);

	for(int i = 0; i < m_joinedUsers.GetItemCount(); ++i)
	{
		SystemAddress* pUserAddress = reinterpret_cast<SystemAddress*>(m_joinedUsers.GetItemData(i));		
		if (pUserAddress)
		{
			GetChatClientDoc().Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, *pUserAddress, false);
		}		
	}
	
	m_chatInput = _T("");

	UpdateData(FALSE);
	
}

BOOL CChatRoomClientDlg::OnInitDialog()
{
	CChatClientBaseDlg::OnInitDialog();

	// TODO:  Add extra initialization here
	m_chatOutput.InsertColumn(0, _T("Name"), LVCFMT_LEFT, 100);
	m_chatOutput.InsertColumn(1, _T("Chat"), LVCFMT_LEFT, 300);	

	m_joinedUsers.InsertColumn(0, _T("User"), LVCFMT_LEFT, 100);
	
	

	GetChatClientDoc().Connect(m_coordinatorServerAddress.ToString(false), m_coordinatorServerAddress.port);
	return TRUE;  // return TRUE unless you set the focus to a control
	// EXCEPTION: OCX Property Pages should return FALSE
}

BOOL CChatRoomClientDlg::PreTranslateMessage(MSG* pMsg)
{
	// TODO: Add your specialized code here and/or call the base class
	

	Packet *packet = NULL;
	while (packet = GetChatClientDoc().Receive())
	{

		switch (RakNet::GetPacketIdentifier(packet))
		{
		case ID_REMOTE_DISCONNECTION_NOTIFICATION:
			GetLogView().WriteLog(_T("Another client has disconnected."));			
			break;
		case ID_REMOTE_CONNECTION_LOST:
			GetLogView().WriteLog(_T("Another client has lost the connection."));
			break;
		case ID_REMOTE_NEW_INCOMING_CONNECTION:
			GetLogView().WriteLog(_T("Another client has connected."));
			break;
		case ID_CONNECTION_REQUEST_ACCEPTED:
			{
				GetLogView().WriteLog(_T("Our connection request has been accepted."));			
				
				registerUserAddressToListCtrl(packet->guid, packet->systemAddress);				
			}
			break;
		case ID_NEW_INCOMING_CONNECTION:
			{
				GetLogView().WriteLog(_T("A connection is incoming."));

				registerUserAddressToListCtrl(packet->guid, packet->systemAddress);

			}
			break;
		case ID_NO_FREE_INCOMING_CONNECTIONS:
			GetLogView().WriteLog(_T("The server is full."));
			break;
		case ID_DISCONNECTION_NOTIFICATION:
			{
				GetLogView().WriteLog(_T("A client has disconnected."));

				ExitRoom(packet->guid);
			}
			break;
		case ID_CONNECTION_LOST:
			GetLogView().WriteLog(_T("A client lost the connection."));
			break;
		case ID_NAT_PUNCHTHROUGH_SUCCEEDED:
			{
				GetLogView().WriteLog(_T("Nat PunchThrough is Succeeded."));
				unsigned char weAreSender=packet->data[1];
				if (weAreSender)
				{
					GetChatClientDoc().Connect(packet->systemAddress.ToString(false),packet->systemAddress.port);
				}
			}
			break;
		case ID_NAT_PUNCHTHROUGH_FAILED:
			{
				GetLogView().WriteLog(_T("Nat PunchThrough is failed."));
				
				unsigned char weAreSender=packet->data[1];
				if (weAreSender)
				{
					GetChatClientDoc().UseProxyModel(*this, m_coordinatorServerAddress, UNASSIGNED_SYSTEM_ADDRESS, packet->guid, 7000);						
				}				
				
			}
			break;
		case ID_CHAT_REQ:
			{
				CString strName;
				CString strChat;

				ChatReq chatPacket;
				strName.Format(_T("%I64u"), packet->guid.g);

				RakNet::BitStream bitStream(packet->data, packet->length, false);
				bitStream.IgnoreBytes(sizeof(MessageID));
				RakNet::ReadStringFromBitStream(bitStream, strChat);
				
				int item = m_chatOutput.InsertItem(m_chatOutput.GetItemCount(), strName);		
				m_chatOutput.SetItem(item, 1, LVIF_TEXT, strChat, 0, 0, 0, 0);
			}			
			break;
		case ID_JOIN_CHATROOM_ACK:
			{
				JoinChatRoomAck joinChatRoomAck;
				RakNet::BitStream bitStream(packet->data, packet->length, false);
				bitStream.IgnoreBytes(sizeof(MessageID));

				bitStream.Read(joinChatRoomAck.m_bSuccess);
				if (joinChatRoomAck.m_bSuccess == true)
				{
					bitStream.Read(joinChatRoomAck.m_userCount);
					for(BYTE i = 0; i < joinChatRoomAck.m_userCount; ++i)
					{
						RakNetGUID userGuid;
						bitStream.Read(userGuid);
						JoinRoom(userGuid);
					}					
				}
				else
				{

				}
			}
			break;
		default:
			{
				CString strChat;
				strChat.Format(_T("Message with identifier %i has arrived."), packet->data[0]);
				GetLogView().WriteLog(strChat.GetBuffer());
			}
			break;
		}		
		
		GetChatClientDoc().DeallocatePacket(packet);
	}

	return CChatClientBaseDlg::PreTranslateMessage(pMsg);
}
void CChatRoomClientDlg::OnBnClickedButtonExitRoom()
{
	// TODO: Add your control notification handler code here
	for(int i = 0; i < m_joinedUsers.GetItemCount(); ++i)
	{
		SystemAddress* pUserAddress = reinterpret_cast<SystemAddress*>(m_joinedUsers.GetItemData(i));
		if (pUserAddress)
		{
			GetChatClientDoc().CloseConnection(*pUserAddress);
		}
	}

	RakNet::BitStream bitStream;
	bitStream.Write(ExitChatRoomReq().GetMessageID());	
	GetChatClientDoc().Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, m_lobbyServerAddress, false);
	
	CChatClientLobbyDlg* pChatClientDlg = new CChatClientLobbyDlg;
	pChatClientDlg->SetChatClientDoc(GetChatClientDoc());
	pChatClientDlg->SetLogView(GetLogView());
	pChatClientDlg->Create(CChatClientLobbyDlg::IDD, GetParent());

	GetChatClientDoc().SwitchDlg(*this, *pChatClientDlg);
	pChatClientDlg->QueryChatRoom();

	
	delete this;
}

void CChatRoomClientDlg::SetServerAddress( SystemAddress& serverAddress )
{
	m_lobbyServerAddress = serverAddress;
}

void CChatRoomClientDlg::JoinRoom( RakNetGUID& userGuid )
{	
	CString str;
	str.Format(_T("%I64u"), userGuid.g);
	int item = m_joinedUsers.InsertItem(m_joinedUsers.GetItemCount(), str);

	if (userGuid == GetChatClientDoc().GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS))
	{
		registerUserAddressToListCtrl(userGuid, GetChatClientDoc().GetSystemAddressFromGuid(userGuid));		
		return;
	}

	GetChatClientDoc().UseHolePunchingModel(userGuid, m_lobbyServerAddress);
}

void CChatRoomClientDlg::OnForwardingSuccess( const char *proxyIPAddress, unsigned short proxyPort, unsigned short reverseProxyPort, SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNet::UDPProxyClient *proxyClientPlugin )
{
	GetLogView().WriteLog(_T("OnForwardingSuccess."));
	GetChatClientDoc().Connect(proxyIPAddress, proxyPort);
}

void CChatRoomClientDlg::OnForwardingNotification( const char *proxyIPAddress, unsigned short proxyPort, unsigned short reverseProxyPort, SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNet::UDPProxyClient *proxyClientPlugin )
{
	GetLogView().WriteLog(_T("OnForwardingNotification."));
}

void CChatRoomClientDlg::OnNoServersOnline( SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNet::UDPProxyClient *proxyClientPlugin )
{
	GetLogView().WriteLog(_T("OnNoServersOnline."));
}

void CChatRoomClientDlg::OnRecipientNotConnected( SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNetGUID targetGuid, RakNet::UDPProxyClient *proxyClientPlugin )
{
	GetLogView().WriteLog(_T("OnRecipientNotConnected."));
}

void CChatRoomClientDlg::OnAllServersBusy( SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNet::UDPProxyClient *proxyClientPlugin )
{
	GetLogView().WriteLog(_T("OnAllServersBusy."));
}

void CChatRoomClientDlg::OnForwardingInProgress( SystemAddress proxyCoordinator, SystemAddress sourceAddress, SystemAddress targetAddress, RakNet::UDPProxyClient *proxyClientPlugin )
{
	GetLogView().WriteLog(_T("OnForwardingInProgress."));
}

void CChatRoomClientDlg::SetCoordinatorAddress( SystemAddress& serverAddress )
{
	m_coordinatorServerAddress = serverAddress;
}
void CChatRoomClientDlg::OnDestroy()
{
	for(int i = 0; i < m_joinedUsers.GetItemCount(); ++i)
	{
		delete reinterpret_cast<SystemAddress*>(m_joinedUsers.GetItemData(i));
	}

	m_joinedUsers.DeleteAllItems();

	__super::OnDestroy();

	// TODO: Add your message handler code here
	
}

void CChatRoomClientDlg::registerUserAddressToListCtrl( const RakNetGUID& userGuid, const SystemAddress& userAddress )
{
	CString strUserGuid;
	strUserGuid.Format(_T("%I64u"), userGuid);

	LVFINDINFO info;
	info.flags = LVFI_PARTIAL|LVFI_STRING;
	info.psz = strUserGuid.GetBuffer();

	int nIndex = m_joinedUsers.FindItem(&info);
	if (nIndex >= 0)
	{
		SystemAddress* pUserAddress = new SystemAddress;
		*pUserAddress = userAddress;
		m_joinedUsers.SetItemData(nIndex, reinterpret_cast<DWORD_PTR>(pUserAddress));
	}
}

void CChatRoomClientDlg::ExitRoom( RakNetGUID& userGuid )
{
	CString strUserGuid;
	strUserGuid.Format(_T("%I64u"), userGuid);

	LVFINDINFO info;
	info.flags = LVFI_PARTIAL|LVFI_STRING;
	info.psz = strUserGuid.GetBuffer();

	int nIndex = m_joinedUsers.FindItem(&info);
	if (nIndex >= 0)
	{
		SystemAddress* pUserAddress = reinterpret_cast<SystemAddress*>(m_joinedUsers.GetItemData(nIndex));		
		
		delete pUserAddress;

		m_joinedUsers.DeleteItem(nIndex);
	}

}